home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 November: Tool Chest / Dev.CD Nov 94.toast / Sample Code / Snippets / Toolbox / ColorCDEF / SampCtrl.P
Encoding:
Text File  |  1992-07-15  |  14.6 KB  |  492 lines  |  [TEXT/MPS ]

  1. Unit SampControl;
  2. {
  3.     Sample Button Defproc for system 7.0
  4.     ©1991 Apple Computer Inc.
  5.     By    Apple Developer Tech Support
  6.     
  7.     This working sample defproc emulates the features of the standard button control for
  8.     system software 7.0 on the macintosh. This defproc, while completely functional is
  9.     intended as an explanatory example to show some of the basics of writing your own
  10.     defproc. 
  11.     
  12.     Items not implemented:
  13.     • Multiple line titles
  14.     • Checking to be sure I am running under system 7.0
  15.     
  16.     
  17.     MPW Build commands:
  18.     Pascal SampCtrl.p
  19.     Link -rt CDEF=128 SampCtrl.p.o -o SampCtrl
  20.  
  21.     Change History:
  22.     
  23.     5/28/91        Mensch    Created this whole thing
  24.     6/3/91        Mensch    Finished adding comments and cleaning up some bugs. could be 1.0
  25.     
  26. }
  27.  
  28. {$R-}
  29.  
  30. Interface
  31.   USES     memtypes, quickdraw, Controls, osintf, toolintf, packintf, gestaltEqu;
  32.   
  33. Function MySampControl(    VarCode:Integer; theControl:ControlHandle;
  34.                         Message:Integer; param:LongInt) : LongInt;
  35.                         
  36. Implementation        
  37. {     The following hack is needed because The entry for the defproc needs to be the first
  38.     executable code, and MPW places sub procedures before main procedures.}
  39.     
  40. { see tech note 256 for the discussion of the QDVarRec information here }
  41. Type    QDVarRecPtr = ^QDVarRec;
  42.         QDVarRec = Record
  43.                     randSeed:Longint;
  44.                     screenBits:Bitmap;
  45.                     arrow:cursor;
  46.                     dkGray:pattern;
  47.                     ltGray:pattern;
  48.                     gray:pattern;
  49.                     black:pattern;
  50.                     white:pattern;
  51.                     thePort:Grafptr;
  52.                    end;
  53.  
  54. Function YouMustBeJoking (a:Integer; b:ControlHandle; c:Integer; d:LongInt):LongInt;
  55.  
  56. begin
  57.     YouMustBeJoking:=MySampControl(a,b,c,d);
  58. end;
  59.  
  60. FUNCTION xGestalt(selector: OSType;VAR response: LONGINT): OSErr; 
  61.     Inline $202F,$0004,$A1AD,$2257,$2288,$3f40,$0008,$508F;
  62.     {
  63.             Move.L Selector(sp),D0
  64.             _Gestalt
  65.             Move.L    response(sp),A1
  66.             Move.L    A0,(A1)
  67.             Move.W    D0,xGestalt(sp)
  68.             AddQ.L    #$8,sp }
  69.  
  70. Procedure GetMyQDVarRec(var a:qdVarRec);
  71. begin
  72.     a:=QDVarRecPtr(LongintPtr(SetCurrentA5)^-(SizeOf(QDVarRec)-SizeOf(thePort)))^;
  73. end;
  74.  
  75. Function MySampControl(    VarCode:Integer; theControl:ControlHandle;
  76.                         Message:Integer; param:LongInt) : LongInt;
  77.                         
  78.  
  79. type    CDPInfo= packed array[0..3] of byte;
  80.  
  81. Var        
  82. { Globals used throughout the sample control go here }
  83.         theResult : Longint;        { set by individual functions/ returned by defproc }
  84.         ResultValid : Boolean;        { If true return theResult else return 0 }
  85.         
  86.         HasColorQD : Boolean;
  87.         
  88.         oldState : PenState;        { The following are for storing the state of the grafport }
  89.         oldTextMode,oldTextSize,oldTextFont : Integer;
  90.         oldClipRgn    : rgnHandle;
  91.         oldForeColor,oldBackColor : RGBColor;
  92.         
  93.         theColorTab: CCTabHandle;    { Color table to use to draw the control }
  94.         wContRGB    : RGBColor;        { the windows content color }
  95.         
  96.         { Use these to suck information from the control record for ease of use }
  97.         IsSimpleButton,isCheckBox,IsRadioBut:Boolean;
  98.         thisCDPInfo    : cdpInfo;
  99.         thisVar        : Integer;    {set to say what type of control this is...}
  100.         thisHiLite    : Integer;    { set to hilite value of the control }
  101.         thisWindow    : windowPtr;
  102.         
  103.         QD            : qdVarRec;    { our current quickdraw "globals" }
  104.         
  105.     Procedure StdDrawSetup;
  106.     { This routine preforms any standard setup that is required by the drawing functions }
  107.     
  108.     var    tempInt:integer;
  109.         tempLong:Longint;
  110.         anErr:OSErr;
  111.         aAuxWin:AuxWinHandle;
  112.         aCTab:CTabHandle;
  113.         theAuxData : AuxCtlHandle; 
  114.         FMDefSizePtr:Ptr;
  115.         SysFontSize:IntegerPtr;
  116.         
  117.     begin
  118.         GetPenState(oldState);
  119.         PenNormal;
  120.         GetMyQDVarRec(QD);
  121.         oldTextSize:=thisWindow^.txSize;
  122.         oldTextFont:=thisWindow^.txFont;
  123.         TextFont(0);                    { This is how we do it, you could set any font/size }
  124.         SysFontSize:=IntegerPtr($BA8);    { that you want }
  125.         FMDefSizePtr:=ptr($987);
  126.         if SysFontSize^<>0 then
  127.           TextSize(SysFontSize^)
  128.         else
  129.           TextSize(Integer(FMDefSizePtr^));
  130.           
  131.         HasColorQD:=false;
  132.         anErr:=xGestalt(GestaltQuickdrawVersion,tempLong);
  133.         if (anErr=0) and (tempLong>=gestalt8BitQD) then HasColorQD:=true;
  134.         if HasColorQD then 
  135.           begin
  136.             oldTextMode:=thisWindow^.txMode;
  137.             GetForeColor(oldForeColor);
  138.             GetBackColor(oldBackColor);
  139.             if GetAuxCtl(theControl,theAuxData) then ; {result unimportant}
  140.             theColorTab:=theAuxData^^.acCTable;
  141.             if GetAuxWin(thisWindow,aAuxWin) then ;{result unimportant}
  142.             aCTab:=aAuxWin^^.awCTable;
  143.             tempInt:=aCTab^^.ctSize;
  144.             while tempInt>=0 do
  145.               begin
  146.                 wContRGB:=aCTab^^.ctTable[tempInt].rgb;
  147.                 if aCTab^^.ctTable[tempInt].value=wContentColor then tempInt:=0;
  148.                 tempInt:=tempInt-1;
  149.               end
  150.           end;
  151.     end;
  152.     
  153.     Procedure StdDrawTearDown;
  154.     
  155.     begin
  156.         if HasColorQD then
  157.           begin
  158.             RGBForeColor(oldForeColor);
  159.             RGBBackColor(oldBackColor);
  160.             TextMode(oldTextMode);
  161.           end;
  162.         TextFont(oldTextFont);
  163.         TextSize(oldTextSize);
  164.         SetPenState(oldState);
  165.     end;
  166.     
  167.     function RoundFactor:Integer;
  168.  {this procedure calculates the rounding factor for the simple button that we are
  169.   drawing. It emulates what the standard control does }
  170.       
  171.     var        tempInt:integer;
  172.     
  173.       begin
  174.         if isSimpleButton then    {only simple buttone are rounded}
  175.           begin
  176.               tempInt:=theControl^^.ContrlRect.Bottom-theControl^^.contrlRect.Top;
  177.             RoundFactor:=tempInt div 2;
  178.           end
  179.         else RoundFactor:=0;    { check boxes and radio buttons get no rounding }
  180.     end;
  181.     
  182.     Procedure DoTestCntl;
  183.     
  184.     begin
  185.         { This routine is called in response to the testCtrl message it simply returns
  186.           the proper part code if the mouse is in the current control rect. }
  187.         if thisHiLite<255 then    { hit test is only valid if the control is enabled }
  188.           begin
  189. {preflight the part code of the result for now..}
  190.             if isCheckBox then
  191.                 theResult:=inCheckBox
  192.             else
  193.               theResult:=inButton;
  194.             ResultValid:=PtInRect(Point(param),TheControl^^.ContrlRect);
  195.           end;
  196.     end;
  197.     
  198.     Procedure DoCalcCRegions;
  199.     
  200.     type    patPtr=^pattern;
  201.     
  202.     var        tempInt:Integer;
  203.             thePatPtr:PatPtr;
  204.             
  205.     begin
  206.         { Called in response to all region calculation routines. This simply sets the passed
  207.           region to the bounding region of the control }
  208.         if not(isSimpleButton) then
  209.             RectRgn(rgnHandle(param),theControl^^.ContrlRect)
  210.         else
  211.           begin
  212.             GetPenState(oldState);
  213.             penNormal;
  214.             hidePen;
  215.             OpenRgn;
  216.             tempInt:=RoundFactor;
  217.             FrameRoundRect(theControl^^.ContrlRect,tempInt,tempInt);
  218.             CloseRgn(rgnHandle(param));
  219.             SetPenState(oldState);
  220.           end;
  221.  {Set the control manager pattern for control dragging. This is a good place to do this
  222.   in case it was screwed up by some other routine. This is done because we do not have our
  223.   own pos proc...}
  224.           thePatPtr:=patPtr($A34);
  225.           thePatPtr^:=QD.gray;
  226.     end;
  227.         
  228.  
  229.     procedure DoDrawIt;
  230.     
  231.     var    roundNess:Integer;
  232.         isDisabled,isHiLited,isNormal:Boolean;
  233.         tempInt:Integer;
  234.         boxWidth:Integer;
  235.         lineHeight:Integer;
  236.         textRect,ctlRect,outerRect:Rect;
  237.         theInfo:FontInfo;
  238.         useGrayText:Boolean;
  239.         aColor:RGBColor;
  240.         
  241.         {Called in response to the DrawCtrl message, this code draws the control in its proper
  242.          state in its proper colors (as defined in the controls color table. the values in the
  243.          table below refer to the control color table value type).
  244.          The proper colors for color controls are as follows (radio & checkboxes are the same):
  245.          
  246.          type/State:        Frame            Text        Background
  247.          CheckBox/normal    cFrameColor        cTextColor    window content color
  248.          CheckBox/Hilited    cFrameColor        cTextColor    window content color
  249.          CheckBox/Disabled    cFrameColor        cTextColor    window content color
  250.          SimpleBut/normal    cFrameColor        cTextColor    cBodyColor
  251.          SimpleBut/HiLited    cFrameColor        cBodyColor    cTextColor
  252.          SimpleBut/Disabled    cFrameColor        cTextColor    cBodyColor
  253.          
  254.          For disabled items, if drawing into a cGrafport then textmode is set to GrayishTextOr,
  255.          otherwise we use the old style overstrike with gray pattern in mode BIC.}
  256.          
  257.          
  258.         procedure GetCtlColor(theValue:integer; VAR theRGB:RGBColor);
  259.         
  260.         var    anInt:integer;
  261.         
  262.         begin
  263.             {Given theValue as the part code to get the color for, this routine returns its
  264.              RGB value. If no color is assigned to the part code, the first color entry is
  265.              returned. This technique can also be used to look up values in window color tables}
  266.             
  267.             anInt:=theColorTab^^.ctSize;
  268.             while anInt>=0 do
  269.               begin
  270.                 theRGB:=theColorTab^^.ctTable[anInt].rgb;
  271.                 if theColorTab^^.ctTable[anInt].value=theValue then anInt:=0;
  272.                 anInt:=anInt-1;
  273.               end
  274.         end;
  275.         
  276.           procedure CalcBackColor;
  277.         
  278.         begin
  279.             {based on the state/status of the control, set the background}
  280.             aColor:=wContRGB;        { Background color for radio/check boxes }
  281.             if isSimpleButton then
  282.               begin
  283.                 if isHiLited then GetCtlColor(cTextColor,aColor)
  284.                 else GetCtlColor(cBodyColor,aColor);
  285.               end;
  286.             If hasColorQD then RGBBackColor(aColor);
  287.         end;
  288.         
  289.           procedure CalcForeColor;
  290.         
  291.         begin
  292.             {based on the state/status of the control, set the foreground}
  293.             if (isHiLited and  isSimpleButton) then GetCtlColor(cBodyColor,aColor)
  294.             else GetCtlColor(cTextColor,aColor);
  295.             If hasColorQD then RGBForeColor(aColor);
  296.         end;
  297.         
  298.         procedure CalcFrameColor;
  299.         
  300.         begin
  301.             {based on the state/status of the control, set the frame(foreground)}
  302.             GetCtlColor(cFrameColor,aColor);
  303.             If hasColorQD then RGBForeColor(aColor);
  304.         end;
  305.         
  306.         procedure CalcTextColor;
  307.         
  308.         begin
  309.             { Set the useGrayText variable if we are ising color quickdraw and the control
  310.               owner is a cGrafPort thisWindow^.PortBits.rowBytes is also thisWindow^.portVersion}
  311.             useGrayText:=false;
  312.             if HasColorQD then
  313.               begin
  314.                 useGrayText:=(BAND(thisWindow^.PortBits.rowBytes,$C000)<>0);
  315.                 if useGrayText and isDisabled then TextMode(GrayishTextOr);
  316.                 CalcForeColor;
  317.               end;
  318.         end;
  319.         
  320.         procedure DrawTheTitle;
  321.         
  322.         begin
  323.             { find out how big the title is and center it in the control rectangle and draw the
  324.               it. this is NOT how the standard control does it.}
  325.             LineHeight:=theInfo.ascent+theInfo.Descent;
  326.             tempInt:=textRect.bottom-textRect.top;
  327.             if lineHeight<tempInt then
  328.               begin
  329.                 textRect.top:=textRect.top+((tempInt-lineHeight) div 2);
  330.                 textRect.Bottom:=textRect.top+LineHeight;
  331.               end;
  332.             if isSimpleButton then
  333.               begin
  334.                 tempInt:=StringWidth(theControl^^.contrlTitle);
  335.                 boxWidth:=textRect.right-textRect.left;
  336.                 if tempInt<boxWidth then
  337.                   begin
  338.                     TextRect.Left:=TextRect.left+((boxWidth-tempInt) div 2);
  339.                   end
  340.                 else
  341.                   TextRect.Left:=TextRect.Left;
  342.               end
  343.             else
  344.               textRect.Left:=textRect.Left+18;
  345.             textRect.Right:=textRect.left+stringWidth(theControl^^.contrlTitle);
  346.             MoveTo(textRect.left,textRect.top+theInfo.ascent);
  347.             DrawString(theControl^^.contrlTitle);
  348.         end;
  349.         
  350.         function ShrinkClip:boolean;
  351.         { Set the clip rgn of the grafport to be the intersection of the current clip rgn
  352.           and the control rectangle. This will insure that we never draw outside of out control
  353.           Return true if this results in an empty region. so that drawing does not take place }
  354.         var    Wally:boolean;
  355.         
  356.         begin
  357.             oldClipRgn:=NewRgn;
  358.             GetClip(oldClipRgn);    {copy the current clip region}
  359.             ClipRect(theControl^^.ContrlRect);
  360.             SectRgn(oldClipRgn,ThisWIndow^.ClipRgn,ThisWIndow^.clipRgn);
  361.             Wally:=EmptyRgn(ThisWindow^.ClipRgn);
  362.             if Wally then
  363.               begin
  364.                 SetClip(oldClipRgn);
  365.                 DisposeRgn(oldClipRgn);
  366.               end;
  367.             ShrinkClip:=Wally;
  368.         end;
  369.                   
  370.         procedure DrawIndBox;
  371.         
  372.         begin
  373.             {Draws the check box or little round radio button and fills it in. NOTE: the filled
  374.              in indicator is drawn in the frame color. With system 7, the indicator is never dimmed
  375.              and it is the only portion of the control that indicated a hilighted item CalcFrameColor
  376.              was called before this routine is called.}
  377.           CalcBackColor;
  378.           EraseRect(OuterRect);
  379.           if isHiLited then pensize(2,2);
  380.           if isCheckBox then FrameRect(OuterRect)
  381.           else FrameOval(outerRect);
  382.           penSize(1,1);
  383.           if theControl^^.contrlValue=0 then exit(DrawIndBox); {If value 0 then don't fill in indicator}
  384.           if isRadioBut then
  385.             begin
  386.               insetRect(outerRect,3,3);
  387.               PaintOval(outerRect);
  388.             end
  389.           else
  390.             begin
  391.               InsetRect(outerRect,1,1);
  392.               MoveTo(outerRect.left,outerRect.top);
  393.               LineTo(outerRect.right,outerRect.Bottom);
  394.               MoveTo(outerRect.right,outerRect.top-1);
  395.               Lineto(outerRect.left-1,outerRect.Bottom);
  396.             end;
  397.         end;
  398.         
  399.         Procedure DisableButton;
  400.         
  401.         begin
  402.             { if the useGrayText indicates that we have not already drawn the text in a
  403.               light gray fashion, then do the old style dimming on it.}
  404.             if UseGrayText then Exit(DisableButton);
  405.             if isSimpleButton then
  406.               begin
  407.                 CalcForeColor;
  408.                 CalcBackColor;
  409.               end;
  410.             InsetRect(CtlRect,1,1);
  411.             PenPat(QD.gray);
  412.             PenMode(patBIC);
  413.             if isSimpleButton then
  414.                PaintRoundRect(ctlRect,roundNess,roundNess)
  415.             else
  416.                PaintRect(textRect);
  417.         end;
  418.         
  419.     begin
  420.         if theControl^^.ContrlVis<>255 then exit(DoDrawIt); {no drawing needed for invisible controls}
  421.     { Set up some drawing variables that are used often for drawing }
  422.         ctlRect:=theControl^^.ContrlRect;
  423.         isDisabled:=(thisHiLite=255);
  424.         isNormal:=(thisHiLite=0);
  425.         isHiLited:=not(isDisabled or isNormal);
  426.         
  427.         if ShrinkClip then exit(DoDrawIt);
  428.         StdDrawSetup;
  429.         roundNess:=roundFactor;
  430.         { Erase the control bounding box }
  431.         if HasColorQD then CalcBackColor;
  432.         { if param=0 then }
  433.           EraseRoundRect(ctlRect,roundNess,roundNess);
  434.         outerRect:=ctlRect;
  435.         textRect:=outerRect;
  436.         if not(isSimpleButton) then
  437.           begin
  438.               tempInt:=(outerRect.bottom-OuterRect.top-12) div 2;
  439.             outerRect.Top:=outerRect.top+tempInt;
  440.             outerRect.Bottom:=outerRect.top+12;
  441.             outerRect.right:=outerRect.Left+12;
  442.           end;
  443.         CalcTextColor;
  444.         GetFontInfo(theInfo);
  445.         DrawtheTitle;
  446.         CalcFrameColor;
  447.         if IsSimpleButton then
  448.             FrameRoundRect(theControl^^.contrlRect,roundNess,roundNess)
  449.         else  DrawindBox;
  450.         if isDisabled then DisableButton;
  451.         { if we are on a black and white machine the button has yet to be inverted if
  452.           it is selected.}
  453.         if Not(HasColorQD) and isHiLited then
  454.           InvertRoundRect(ctlRect,roundNess,roundNess);
  455.         SetClip(oldClipRgn);
  456.         DisposeRgn(oldClipRgn);
  457.         StdDrawTearDown;
  458.     end;
  459.     
  460. Begin
  461.     theResult:=0;
  462.     ResultValid:=false;
  463.     thisCDPInfo:=cdpInfo(theControl^^.ContrlDefProc);
  464.     thisWindow:=theControl^^.contrlOwner;
  465.     thisHiLite:=theControl^^.ContrlHiLite;
  466.     isSimpleButton:= thisCDPInfo[0]=0;
  467.     isCheckBox:= thisCDPInfo[0]=1;
  468.     isRadioBut:= thisCDPInfo[0]=2;
  469.     Case Message of
  470.         drawCntl    : DoDrawIt;
  471.         testCntl    : DoTestCntl;
  472.         calcCRgns    : DoCalcCRegions;
  473.         initCntl     : ;        { can be used to initialize special control data structures }
  474.         dispCntl    : ;        { If you need to clean up anything before you finish }
  475.         posCntl     : ;        { If you don't want standard control mgr moveing }
  476.         thumbCntl     : ;        { for use with custom thumb dragging routines }
  477.         dragCntl     : ;        { for custom dragging of the control, or parts of }
  478.         autoTrack     : ;        { a default track control function for all controls of this type }
  479.         calcCntlRgn,
  480.         calcThumbRgn: begin
  481.                            ResultValid:=true;
  482.                         theResult:=1;
  483.                         DoCalcCRegions;
  484.                       end;
  485.       Otherwise ;
  486.     end; {Case}
  487.     If ResultValid then MySampControl:=theResult
  488.       else MySampControl:=0;
  489. End;
  490.  
  491. End.
  492.